모듈 라이선스 관리
어셈블리 서명: 라이선스 발급을 위해 어셈블리는 서명이 필요하므로, 프로젝트 파일에 다음과 같이 모듈 명과 동일하게 서명 파일을 만들고 설정을 추가합니다. modulename.snk
서명 파일은 프로젝트 루트에 위치해야 합니다.
sn -k modulename.snk
.NET 어셈블리에 서명(Strong-Name Signing)을 하는 주된 이유는 다음과 같습니다.
- 고유한 신원 (Unique Identity) 및 이름 충돌 방지
- 무결성 보장 (Integrity Assurance)
- 신뢰의 원천 (Source of Trust)
- GAC (Global Assembly Cache) 등록 필수
- 버전 관리 및 Side-by-Side 실행
- 보안 정책 (Code Access Security)
<PropertyGroup>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>modulename.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
.snk 파일은 공개 키와 개인 키 쌍을 포함하고 있으며, 어셈블리에 서명하는 데 사용됩니다. 공개 키는 어셈블리의 신원을 확인하는 데 사용되고, 개인 키는 서명을 생성하는 데 사용됩니다.
서명 파일이 유출되면, 누구나 해당 서명자를 사칭하여 어셈블리를 위조하거나 변조할 수 있게 됩니다. .snk 파일이 유출되지 않도록 주의해야 합니다.
주의: .snk 파일은 민감한 정보이므로, 절대 공개 된 관리 시스템(예: Git, Svn)에 커밋하지 마십시오. 대신, 비밀 관리 시스템이나 안전한 위치에 보관하고 필요할 때만 사용하십시오.
만약 .snk 파일을 유출 되거나 분실한 경우, 해당 서명으로 서명된 어셈블리를 더 이상 업데이트할 수 없으므로, 이전 버전에 대한 호환성 업무를 검토하고 대응 계획 수립 후에, 새로운 키 쌍을 생성하고 어셈블리를 다시 서명해야 합니다.
모듈 라이선스 키는 무엇인가?
모듈 라이선스 키는 모듈을 특정 고객사에 독점적으로 사용하도록 허가하는 고유한 키입니다. 이 키는 모듈의 코드와 설정을 암호화하는 데 사용되며, 라이선스 키가 없으면 모듈을 사용할 수 없습니다. 이를 통해 모듈의 무단 복제 및 배포를 방지하고, 개발사의 기술 보호를 강화합니다.
HandStack 에서 제공하는 dbclient, function, transact 모듈의 Contract 코드 및 설정은 모두 모듈 라이선스 키로 암호화되어 배포됩니다. 각 고객사는 고유한 라이선스 키를 발급받아 해당 모듈을 사용할 수 있습니다.
그러나 모듈 라이선스 키는 모듈 소프트웨어의 복제 방지 및 무단 사용을 막기 위한 수단일 뿐, HandStack 플랫폼의 라이선스 정책과는 별개입니다. HandStack 플랫폼은 MIT 라이선스 정책에 따라 오픈 소스로 사용되며, 모듈 라이선스 키는 해당 플랫폼의 사용 권한을 대체하지 않습니다.
모듈 라이선스 키는 서버 측과, 클라이언트 측에서 각각 검증을 위한 코드가 포함되어 있습니다. 서버 측에서는 ack 서버가 모듈을 로드할 때 라이선스 키를 검증하고, 클라이언트 측에서는 업무 화면이 실행될 때 라이선스 키를 검증합니다.
서버 측 라이선스 키 예
{
"Licenses": {
"custom-api-module": {
"CompanyName": "HandStack",
"ProductName": "CustomApiModule-v1.0.0-PROD001",
"AuthorizedHost": "handstack.kr,www.handstack.kr",
"Key": "NDJlNjE2YjE2N2FmMjA3ZmZkN2I0NmQ4ZTFhM2IyZjU6YTUwYTc2ZGQ4MjZhYjZkM2YxNmQwMThkNzA4ODVhYmE2M2NkY2IyZmNkNzg1ZGE0OWUxYzk3MTAwOWMwMTc1N2JmMjBiZWU3M2EzZDFhMTRkNGI4NWM1ZTA2NzBkNmQ0ZWQyMWQ2NjRiMTMxOWQ3OGM1ZWFmYWY3YTUzMjRlNTliNDg5M2FlYjgyMzhjMmY4YTFjMDVmOGIzOGJiZTJjZTc0OTc5ODRiYWQ5YzA3Mjc5OWY5ZTA0ZWFjZTQyM2E4NmE3YWM1YzdiZDg3MmFkMGU4NWY1ZGNlMTBlZmFiMGExNThjZGU2Nzk3OGUzMDNlNDExNWM0MmY2ZGIwMzkzMg==",
"CreatedAt": "2025-08-25T03:22:12.304Z",
"ExpiresAt": "2026-07-01T23:59:59.000Z",
"Environment": "Production",
"SignKey": "ac3263d40c5950c94302f738cf8bd1ef82613b3fb8c9e14a3e456620d3f327cc.handstack-salt-value"
}
},
"GeneratedAt": "2025-08-25T03:22:12.328Z",
"GeneratedBy": "handstack",
"Version": "1.0.0"
}
이 정보는 ack 서버가 시작될 때 appsettings.json
파일에서 읽어들여, 모듈을 로드 할 때 해당 모듈의 라이선스 키를 검증하는 데 사용됩니다.
{
...
"LoadModules": [
"wwwroot",
"transact",
"dbclient",
"function",
"repository",
"logger",
"custom-api-module"
],
"LoadModuleLicenses": {
"custom-api-module": {
"CompanyName": "HandStack",
"ProductName": "CustomApiModule-v1.0.0-PROD001",
"AuthorizedHost": "handstack.kr,www.handstack.kr",
"Key": "NDJlNjE2YjE2N2FmMjA3ZmZkN2I0NmQ4ZTFhM2IyZjU6YTUwYTc2ZGQ4MjZhYjZkM2YxNmQwMThkNzA4ODVhYmE2M2NkY2IyZmNkNzg1ZGE0OWUxYzk3MTAwOWMwMTc1N2JmMjBiZWU3M2EzZDFhMTRkNGI4NWM1ZTA2NzBkNmQ0ZWQyMWQ2NjRiMTMxOWQ3OGM1ZWFmYWY3YTUzMjRlNTliNDg5M2FlYjgyMzhjMmY4YTFjMDVmOGIzOGJiZTJjZTc0OTc5ODRiYWQ5YzA3Mjc5OWY5ZTA0ZWFjZTQyM2E4NmE3YWM1YzdiZDg3MmFkMGU4NWY1ZGNlMTBlZmFiMGExNThjZGU2Nzk3OGUzMDNlNDExNWM0MmY2ZGIwMzkzMg==",
"CreatedAt": "2025-08-25T03:22:12.304Z",
"ExpiresAt": "2026-07-01T23:59:59.000Z",
"Environment": "Production",
"SignKey": "ac3263d40c5950c94302f738cf8bd1ef82613b3fb8c9e14a3e456620d3f327cc.handstack-salt-value"
}
}
...
}
클라이언트 측 라이선스 키 예 (customApiModuleLicense.js)
/*!
* Product ID: CustomApiModule-v1.0.0-PROD001
* Authorized Domain(or IP): handstack.kr,www.handstack.kr
* Publisher: handstack.kr
* Generated: 2025-08-25 03:22:12 UTC
* Generated By: handstack
* Module ID: custom-api-module
* Environment: Production
* Expires: 2026-07-01T23:59:59.000Z
*/
/* eslint-disable */
var customApiModuleLicense = "NDJlNjE2YjE2N2FmMjA3ZmZkN2I0NmQ4ZTFhM2IyZjU6YTUwYTc2ZGQ4MjZhYjZkM2YxNmQwMThkNzA4ODVhYmE2M2NkY2IyZmNkNzg1ZGE0OWUxYzk3MTAwOWMwMTc1N2JmMjBiZWU3M2EzZDFhMTRkNGI4NWM1ZTA2NzBkNmQ0ZWQyMWQ2NjRiMTMxOWQ3OGM1ZWFmYWY3YTUzMjRlNTliNDg5M2FlYjgyMzhjMmY4YTFjMDVmOGIzOGJiZTJjZTc0OTc5ODRiYWQ5YzA3Mjc5OWY5ZTA0ZWFjZTQyM2E4NmE3YWM1YzdiZDg3MmFkMGU4NWY1ZGNlMTBlZmFiMGExNThjZGU2Nzk3OGUzMDNlNDExNWM0MmY2ZGIwMzkzMg==.ac3263d40c5950c94302f738cf8bd1ef82613b3fb8c9e14a3e456620d3f327cc.handstack-salt-value";
if (typeof window !== "undefined") window.customApiModuleLicense = customApiModuleLicense;
이 정보는 클라이언트 측에서 업무 화면이 실행될 때 라이선스 키를 검증하는 데 사용됩니다.
모듈 라이선스 키 발급 하기
HandStack 2025.8.25 버전부터는 고객사별로 고유한 모듈 라이선스 키를 발급하여, 모듈의 코드 및 설정을 암호화하는 방식을 도입하였습니다. 이를 통해 모듈의 무단 복제 및 배포를 방지하고, 개발사의 기술 보호를 강화합니다.
handstack CLI 도구로 공개 키 확인
모듈 소프트웨어의 공개 키는 HandStack handstack/4.Tool/CLI/handstack
디렉토리에 있는 handstack
CLI 도구를 사용하여 확인 할 수 있습니다. 이 도구는 .NET 환경에서 실행되며, 다음과 같은 명령어로 사용할 수 있습니다.
handstack publickey --file="C:\projects\company\modules\modulename\bin\Debug\net8.0\modulename.dll"
어셈블리 파일 경로: C:\projects\company\modules\modulename\bin\Debug\net8.0\modulename.dll
어셈블리 이름: modulename
어셈블리 버전: 1.0.0.0
강력한 이름 서명: 예
공개 키 (Hex):
00240000048000009400000006020000002400005253413100040000010001002D5CE5F5F32A43756616EB6619AF8F15FCB8ABA785AA38919E919617C93B2048254D3CBED2F27D1C91AA0876758766A9DE6A357853883ACA677A0AC7399C8466C5345E4F68228BDA5380B974451ACE1413B8CC7E22778E69FBBE6A609169A3E9EF798EB6C21B37C793F8A66C361F43ABDE0BD27171EC59F14574D39D40EB01B7
공개 키 (Base64):
ACQAAASAAACUAAAABgIAAAAkAABSU0ExAAQAAAEAAQAtXOX18ypDdWYW62YZr48V/Lirp4WqOJGekZYXyTsgSCVNPL7S8n0ckaoIdnWHZqneajV4U4g6ymd6Csc5nIRmxTReT2gii9pTgLl0RRrOFBO4zH4id45p+75qYJFpo+nveY62whs3x5P4pmw2H0Or3gvScXHsWfFFdNOdQOsBtw==
공개 키 (SHA256):
e066b046f40c9f1fd0c263265227be9e068a73be1f403e482f484fbc450148b9
공개 키 (Token):
b9af6de54c4bdeb3
지적 재산권 보호를 위한 개발사 정보를 변경
텍스트 편집기로 handstack/4.Tool/CLI/node-cli/license-cli/license-manager.js
파일을 열고, 다음과 같은 기본 설정 값을 개발사에 맞게 변경합니다.
this.saltValue = 'handstack-salt-value';
this.publisher = 'handstack.kr';
this.allowedDomains = ['localhost', '127.0.0.1'];
this.currentUser = 'handstack';
- saltValue: 라이선스 키 생성 및 검증에 사용되는 솔트 값입니다. 개발사의 모듈 소프트웨어의 공개 키을 설정합니다. (예: e066b046f40c9f1fd0c263265227be9e068a73be1f403e482f484fbc450148b9)
- publisher: 라이선스 키에 포함될 발행자 정보입니다. 개발사의 도메인이나 이름을 설정합니다.
- allowedDomains: 라이선스 키에 포함될 허용된 도메인 목록입니다. 개발사의 도메인이나 IP 주소를 설정합니다.
- currentUser: 라이선스 키 생성 시 기록될 사용자 정보입니다. 개발사의 이름이나 ID를 설정합니다.
license-cli.js 도구 사용
모듈 라이선스 키는 HandStack handstack/4.Tool/CLI/node-cli/license-cli
디렉토리에 있는 license-cli.js
CLI 도구를 사용하여 발급할 수 있습니다. 이 도구는 Node.js 환경에서 실행되며, 다음과 같은 명령어로 사용할 수 있습니다.
cd handstack/4.Tool/CLI/node-cli/license-cli
npm install
node license-cli.js create --module-id "custom-api-module" --company "HandStack" --product "CustomApiModule-v1.0.0-PROD001" --hosts "handstack.kr,www.handstack.kr" --environment "Production" --expires "2026-07-01T23:59:59.000Z" --gen-js --js-dir "./generated-licenses"
node license-cli.js create --module-id "custom-api-module" --company "HandStack" --product "CustomApiModule-v1.0.0-PROD001" --hosts "handstack.kr,www.handstack.kr" --environment "Production" --expires "2026-07-01T23:59:59.000Z" --gen-js --js-dir "./generated-licenses"
위의 명령을 실행하면, 지정한 모듈 ID, 회사명, 제품명, 허용된 호스트, 환경, 만료일 등을 기반으로 모듈 라이선스 키가 생성됩니다. --gen-js
옵션을 사용하면 클라이언트 측에서 사용할 수 있는 JavaScript 파일도 generated-licenses 디렉토리에 함께 생성됩니다.
여기에서 개발사에 맞게 어셈블리 서명을 하여 [모듈 소프트웨어 공개 키]를 포함해서 개발사의 지적 재산권 보호를 위한 추가 정보를 변경 해야 합니다. 이 공개 키는 모듈 소프트웨어의 무결성을 검증하는 데 사용됩니다.
모듈 라이선스 키로 dbclient, function, transact 모듈 Contract 코드 및 설정 암호화 하기
외부로 배포되는 모듈 애플리케이션을 개발할 때는 주요 코드 (C#, JavaScript) 에 대해 난독화 도구를 사용하는 것을 검토하는 권장합니다. 또한, 모듈 라이선스 키로 Contract 코드 및 설정을 암호화하여, 무단 복제 및 배포를 방지하고, 개발사의 기술 보호를 강화할 수 있습니다.
모듈 라이선스 키로 dbclient, function, transact 모듈의 Contract 코드 및 설정을 암호화하려면 HandStack handstack/4.Tool/CLI/handstack
디렉토리에 있는 handstack
CLI 도구를 사용하여 확인 할 수 있습니다. 이 도구는 .NET 환경에서 실행되며, 다음과 같은 명령어로 사용할 수 있습니다.
handstack encryptcontracts --file="C:\projects\company\modules\modulename\bin\Debug\net8.0\modulename.dll" --directory="C:\projects\company\modules\modulename\bin\Debug\net8.0\Contracts"
위의 명령을 실행하면, 지정한 모듈 어셈블리 파일 경로와 Contract 디렉토리를 기반으로 Contract 코드 및 설정이 암호화됩니다. 이때, 모듈 어셈블리 파일에 포함된 공개 키와 토큰 키를 사용하여 암호화가 수행됩니다.
암호화된 Contract 예제
dbclient (XML)
<?xml version="1.0" encoding="utf-8"?>
<mapper xmlns="contract.xsd">
<header>
<application>HDS</application>
<project>TST</project>
<transaction>MYS010</transaction>
<datasource>DB04</datasource>
<use>Y</use>
<desc>MySQL 거래 테스트</desc>
<signaturekey>b9af6de54c4bdeb3</signaturekey>
<encryptcommands>Dooo4WTlsCFhT474P4TpZ3a8aBzCH3PdO8opotQKDTHbvFar4czbsuMyv2GTpWU9yZfWJMJoEcHWRPeiEpYxnQ99hI2gQNXieueZIZ50DGSteAq64vIwlkMZCE2zqsUv+LoWq87dlHEoOgYLrSka2YFCqIJt12wc0YpT4W6jDv5+TRf0s5H3IU1XEmqvxymwJIXetlmvSdL1m3Fhniqr/fjpK/il0Bh9B0r16tCyfW0a0PDBF6t0JIeeGK1VrxukOgkr9LSRfk6F7fhpP6eD87pjZGLShID551h/Guc6ymk=</encryptcommands>
</header>
<commands></commands>
</mapper>
transact (JSON)
{
"ApplicationID": "HDS",
"ProjectID": "BOD",
"TransactionID": "BOD010",
"Comment": "게시글 목록 거래",
"Services": [],
"Models": [],
"SignatureKey": "b9af6de54c4bdeb3",
"EncryptServices": "V53JUvsmh/ZpCOeEtQVMmhgMbffMykl2wO6UhAaRnTNh02QyOtE4YGkT3hd5fW1kX1voquoxxoXfc9Qcda/jtFfcHo8DwJ7xvSweKdshWocaThQ+QRMmWs0YY0UQAbjBrhV2xY97HmVbSPb4f9+E8OeKF2jGYYucdLMEMtmlEhmGbdfOiK/gij/IHbZAnAIjdZ/bbaJFsd8ECQmg+hnL+VWecCJNEzVIyqoQ1D6RLoWRX9niB+ExPEaizlqqj6+gcJbQRiChBlgTZ0qd74VmGyG1OP8CG9U5OZ8tUqlJY6vNpNEmGnZGDcRzAH6sKapNHgum9IZpS1x2kUEJIpaUGcLUn/PnZRcpf3prswdxwxbXlS8x4CDGndYjiys0Xa6VtoDEDz6pJZIlmHka9o+yroSE5Pfw89nKyo62wJbELX5/k+bH33K953j6cgDgEFHE7sxc4mRSpd0CsBlkDNQg37JnvzN2IZvrguW5fBVpO3s="
}
function (featureMeta.json)
{
"Header": {
"ApplicationID": "HDS",
"ProjectID": "TST",
"TransactionID": "JSF010",
"DataSourceID": "FN01, FN02, FN04",
"LanguageType": "javascript",
"Use": true,
"Comment": "Javascript 거래 함수 테스트",
"SignatureKey": "b9af6de54c4bdeb3",
"EncryptCommands": "0jzHnRoJ7eYg8DCSgvX3HnZyWmFs3+1slwgd9l2J0s+N1oHWS+pH68cUyoYbvYJuz+NXAgFKUZLXBnsJKMqpUfXaZnkZv8DdgQznzqzpIM5KGN4xv8OLeeDZ9g2ETC/vWwzWPXuPCZyeKRme64jOQS40Ha5zTT+/fFlzfgUfAzNB4d5u+FbkDm5VME1kmvjquwUOqpuuUYfCgD6PmKi6THP/5dI/z/9o4RmYoeK3404="
},
"Commands": []
}
.cs, .js, .py 와 같은 소스 코드 파일은 다음과 같은 이유로 암호화 하지 않습니다.
- 개발 및 유지보수의 어려움: 개발자가 서버 측 코드를 이해하고, 수정하고, 디버깅하기 위한 과정이 매우 복잡해집니다.
- 컴파일 및 실행 환경의 문제: 코드를 실행하려면 결국 복호화된 형태로 존재해야 합니다. 암호화된 코드를 실행하기 위해서는 매번 복호화 과정을 거쳐야 하는데, 이는 성능 저하를 야기합니다.
- 보안 측면의 한계: HandStack 은 오픈 소스로 개발되며, 소스 코드 공개를 통해 투명성, 협력, 보안 감사를 장려합니다. 코드를 암호화하면 복호화 키가 필요합니다. 이 키를 안전하게 관리하는 것이 또 다른 보안 문제가 됩니다.
소스 코드 파일 자체를 암호화하는 것은 개발 효율성을 극도로 저해하고, 완벽한 보안을 제공하지 못하며, 다른 효과적인 보호 방법들이 존재하기 때문에 일반적으로 사용되지 않습니다.
실제적 인 소스 코드 보호 방법
소프트웨어 개발자들이 지적 재산권을 보호하거나 중요한 로직을 숨기고자 할 때는 다음과 같은 방법을 사용합니다.
- 컴파일된 코드 배포: C#, Java 등은 소스 코드를 중간 언어(IL) 또는 기계어(바이트 코드)로 컴파일하여 배포합니다. 이 컴파일된 코드는 원본 소스 코드만큼 읽기 쉽지 않습니다.
- 난독화 (Obfuscation): 컴파일된 코드(예: .NET 어셈블리, Java JAR)를 역컴파일하거나 디컴파일하기 어렵게 만듭니다. 변수명, 함수명 등을 의미 없는 문자열로 바꾸고, 코드 흐름을 복잡하게 만들어 분석을 어렵게 합니다.
- 핵심 로직 서버 측에서 실행: 중요한 알고리즘이나 비즈니스 로직은 클라이언트(사용자) 측이 아닌 서버 측에서 실행되도록 설계하여 노출을 방지합니다.
- 라이센스 계약: 법적인 보호 장치인 라이센스 계약을 통해 소스 코드 무단 사용 및 배포를 금지합니다.
프로그램 에서 모듈 라이선스 키 검증 흐름
- ack 서버 모듈 로드 할 때 라이선스 키 검증
- 암호화 된 Contract (dbclient, function, transact) 코드 및 설정 로드 할 때 라이선스 키 확인 후 복호화 로드
- 업무 화면 실행 할 때 JavaScript 라이선스 키 검증
직접 모듈 라이선스 키 검증 하기
licenses.json 파일에 수급한 라이선스 키를 등록하고, 직접 검증하는 방법은 다음과 같습니다.
node license-cli.js validate --module-id "handstack-ui-v1" --file "./licenses.json"
웹 브라우저에서 JavaScript로 직접 검증하는 방법은 다음과 같습니다.
텍스트 편집기로 handstack/4.Tool/CLI/node-cli/license-cli/demo/license-validator-browser.js
파일을 열고, 다음과 같은 기본 설정 값을 개발사에 맞게 변경합니다.
this.saltValue = 'handstack-salt-value';
this.publisher = 'handstack.kr';
this.allowedDomains = ['localhost', '127.0.0.1'];
this.currentUser = 'handstack';
license-validation-demo.html 파일에서 customApiModuleLicense.js
와 같이 JavaScript 라이선스 키 파일을 포함하고, 코드를 참고하여 개발사의 환경에 맞게 검증할 수 있습니다.